package uk.ac.cam.djjb2.mygame;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class ServerNetManager extends NetManager {
	private class ClientImpl implements Client {
		private final int id;
		private final Socket socket;
		private final DataInputStream inputStream;
		private final DataOutputStream outputStream;
		private final Thread thread;
		private Player player;
		
		private ClientImpl(Socket socket) throws IOException {
			this.id = ServerNetManager.this.clients.put(this);
			this.socket = socket;
			this.inputStream = new DataInputStream(socket.getInputStream());
			this.outputStream = new DataOutputStream(socket.getOutputStream());
			
			ServerNetManager.this.clientConnected(this);
			
			thread = new Thread(new Runnable() {
				@Override
				public void run() {
					ClientImpl.this.run();
				}
			});
			thread.start();
		}
		
		@Override
		public MessageOutputStream createMessageStream() {
			return new MessageOutputStream(outputStream);
		}
		
		@Override
		public int getId() {
			return id;
		}

		private void run() {
			try {
				while (!socket.isClosed()) {
					MessageInputStream message = new MessageInputStream(inputStream);
					ServerNetManager.this.postMessage(this.id, message);
				}
			
			} catch (IOException e) {
				try {
					if (!socket.isClosed())
						socket.close();
				} catch (IOException e1) {
				}
			}
			
			ServerNetManager.this.clients.remove(id);
			ServerNetManager.this.clientDisconnected(this);
		}

		@Override
		public void setPlayer(Player player) {
			this.player = player;
		}

		@Override
		public Player getPlayer() {
			return this.player;
		}
	}
	
	private ServerSocket serverSocket;
	private Thread serverThread;
	private IdManager<ClientImpl> clients = new IdManager<ClientImpl>();
	private Set<ServerComponent> components = new HashSet<ServerComponent>();
	
	public void addComponent(ServerComponent component) {
		components.add(component);
	}
	public void removeComponent(ServerComponent component) {
		components.remove(component);
	}
	
	public ServerNetManager(int port) throws IOException {
		serverSocket = new ServerSocket(port);
		
		serverThread = new Thread(new Runnable() {
			public void run(){
				serverThread();
			}
		});
		serverThread.start();
	}
	
	private void clientConnected(Client client) {
		for (ServerComponent component : components) {
			component.clientConnected(client);
		}
	}

	private void clientDisconnected(Client client) {
		for (ServerComponent component : components) {
			component.clientDisconnected(client);
		}
	}
	
	private void serverThread() {
		while (!serverSocket.isClosed()) {
			try {
				Socket socket = serverSocket.accept();
				new ClientImpl(socket);
			} catch (IOException e) {
			}
		}
	}
	
	public Set<Integer> getClientIds() {
		return clients.getIds();
	}

	public Collection<ClientImpl> getClients() {
		return clients.getObjects();
	}
	
	public Client getClient(int id) {
		return clients.get(id);
	}
	
	public int getLocalPort() {
		return serverSocket.getLocalPort();
	}
}
